/*
* Copyright (c) 2008 Thierry Coppey, EPFL
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Thierry Coppey nor EPFL, nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THIERRY COPPEY AND EPFL ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THIERRY COPPEY OR EPFL BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#import "NMap.h"
#define BUFSIZE 2048

void* nmap_thr(void* arg) {
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
	NMap* nm=(NMap*)arg; NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
	int i=0; char buf[BUFSIZE],*b=buf,*a[BUFSIZE/8]; memset(a,0,sizeof(a)); strlcpy(buf,[[nm query] UTF8String],BUFSIZE);
	do { while (*b==' ') { *b=0; b++; } if (*b) a[i++]=b; } while ((b=strchr(b,' '))&&i<BUFSIZE/8);
	int n,f,p[2]; pipe(p);
	switch(f=fork()) {
		case -1: close(p[1]); goto cleanup;
		case  0: dup2(p[1],1); dup2(p[1],2); close(p[1]); close(p[0]);
			
			execvp("nmap",a);
			
			// my file's real location
			execvp("/usr/local/bin/nmap",a);
			
			_exit(1);
	}
	[nm setPid:f]; close(p[1]);
	while (n=read(p[0],buf,BUFSIZE-1)) { [nm addResult:[NSString stringWithFormat:@"%s",buf]]; memset(buf,0,n); }
	waitpid(f,0,0); [nm setPid:0];
	cleanup: close(p[0]); [pool release]; pthread_exit(0); return 0;
}


@implementation NMap

- (id)init { if (self = [super init]) { nmap_pid=0; } return self; }
- (void)dealloc { if (nmap_pid) { kill(nmap_pid,SIGKILL); pthread_cancel(nmap_th); } [super dealloc]; }
- (void) addResult:(NSString*)text { [output setString:[[output string] stringByAppendingString:text]]; }
- (void) setPid:(int)pid { if (!pid) [scan setTitle:@"Scanner"]; nmap_pid=pid; }

- (IBAction) changeOpts:(id) Sender {
	[di_opts setEnabled:[di intValue]];
	[sc_opts setEnabled:[sc intValue]];
	[sc_idle setEnabled:[sc intValue]&&[sc_opts indexOfSelectedItem]==10];
	[sc_svcs setEnabled:[sc intValue]];
	[sc_port setEnabled:[sc intValue]&&[sc_svcs indexOfSelectedItem]==2];
	[in_scr setEnabled:[in_run intValue]];
	[if_decoys setEnabled:[if_dec intValue]];
	[if_spoof setEnabled:[if_spf intValue]];
	[mi_path setEnabled:[mi_log indexOfSelectedItem]];
}

- (NSString*) query {
	NSMutableString* r=[[NSMutableString alloc] initWithString:@"nmap"];
	// discovery
	bool has_more=[sc intValue]||[in_svc intValue]||[in_os intValue]||[in_run intValue];
	if ([di intValue]) {
		if (!has_more) [r appendString:@" -sP"]; // ping only
		switch([di_opts indexOfSelectedItem]) {
			case 1: [r appendString:@" -PS"]; break; // TCP SYN
			case 2: [r appendString:@" -PA"]; break; // TCP ACK
			case 3: [r appendString:@" -PU"]; break; // UDP ping
			case 4: [r appendString:@" -PE"]; break; // ICMP echo
			case 5: [r appendString:@" -PP"]; break; // ICMP timestamp
			case 6: [r appendString:@" -PM"]; break; // ICMP netmask
			case 7: [r appendString:@" -PO"]; break; // IP protocol
			case 8: [r appendString:@" -PR"]; break; // ARP ping
		}
	} else {
		if (has_more) [r appendString:@" -PN"]; // ignore(always online)
		else [r appendString:@" -sL"]; // list only
	}
	if ([di_route intValue]) [r appendString:@" --traceroute"];
	// portscan
	if ([sc intValue]) {
		switch ([sc_opts indexOfSelectedItem]) {
			case 0: [r appendString:@" -sT"]; break; // TCP connect
			case 1: [r appendString:@" -sS"]; break; // TCP SYN
			case 2: [r appendString:@" -sU"]; break; // UDP scan
			case 3: [r appendString:@" -sN"]; break; // TCP null
			case 4: [r appendString:@" -sF"]; break; // FIN
			case 5: [r appendString:@" -sX"]; break; // XMas
			case 6: [r appendString:@" -sA"]; break; // TCP ACK
			case 7: [r appendString:@" -sW"]; break; // TCP Window
			case 8: [r appendString:@" -sM"]; break; // TCP Maimon
			case 9: [r appendString:@" -sO"]; break; // IP protocol
			case 10: [r appendFormat:@" -sI %@",[sc_idle stringValue]]; break; // Idle scan
		}
		switch ([sc_svcs indexOfSelectedItem]) {
			case 1: [r appendString:@" -F"]; break; // fast scan
			case 2: [r appendFormat:@" -p %@",[sc_port stringValue]]; break; // custom ports
		}
	}
	if ([in_svc intValue]) [r appendString:@" -sV"]; // services version
	if ([in_os intValue]) [r appendString:@" -O"]; // OS version
	if ([in_run intValue]) switch ([in_scr indexOfSelectedItem]) {
		case 0: [r appendString:@" -sC"]; break; // get infos
		case 1: [r appendString:@" --script malware"]; break; // malware
		case 2: [r appendString:@" --script vulnerability"]; break; // vulnerabilities
		case 3: [r appendString:@" --script version"]; break; // versions
		case 4: [r appendString:@" --script all"]; break; // all
	}
	// ids/firewall
	if ([if_frag intValue]) [r appendString:@" -f"];
	if ([if_dec intValue]) [r appendFormat:@" -D %@",[if_decoys stringValue]];
	if ([if_spf intValue]) [r appendFormat:@" -S %@",[if_spoof stringValue]];

	switch ([mi_dns indexOfSelectedItem]) {
		case 1: [r appendString:@" -n"]; break;
		case 2: [r appendString:@" -R"]; break;
	}
	[r appendFormat:@" -T%d",[mi_speed indexOfSelectedItem]];
	if ([mi_log indexOfSelectedItem]) {
		switch ([mi_log indexOfSelectedItem]) {
			case 1: [r appendString:@" -oN"]; break; // NMap
			case 2: [r appendString:@" -oX"]; break; // XML
			case 3: [r appendString:@" -oS"]; break; // ScriptKiddie
			case 4: [r appendString:@" -oG"]; break; // Grepable
			case 5: [r appendString:@" -oA"]; break; // All
		}
		[r appendFormat:@" %@",[mi_path stringValue]];
	}
	if ([mi_more intValue]) [r appendString:@" -v"]; // verbose
	[r appendFormat:@" %@",[hosts stringValue]];
	return r;
}

- (IBAction) startScan:(id) Sender {
	if (nmap_pid) {
		pthread_cancel(nmap_th); kill(nmap_pid,SIGKILL); nmap_pid=0;
		[scan setTitle:@"Scanner"];
		//[scan setLabel:@"Scanner"];
		//[capture setImage:[[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"SniffPlay" ofType:@"png"]]];
	} else {
		pthread_create(&nmap_th,nil,nmap_thr,self);
		[scan setTitle:@"Arrêter"];
		//[capture setImage:[[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"SniffStop" ofType:@"png"]]];
	}
	[output setString:@""];
}
@end
